package com.gitee.wsl.common.ui.graphics.color


import androidx.compose.ui.graphics.Color
import com.gitee.wsl.common.ui.ext.color.rgb
import kotlinx.serialization.Serializable
import kotlin.math.max
import kotlin.math.min

/**
 * A color using hue, saturation, and lightness color spaces
 * @property hue the hue channel in a range between `0.0` and `1.0`
 * @property saturation the saturation channel in a range between `0.0` and `1.0`
 * @property lightness the lightness channel in a range between `0.0` and `1.0`
 * @property alpha the alpha channel in a range between `0.0` and `1.0`
 */
@Serializable
data class HSLColor(val hue: Double, val saturation: Double, val lightness: Double, val alpha: Double = 1.0)

/**
 * Gets the [HSLColor] equivalent to this [Color.RGBColor]
 */
val Color.hsl: HSLColor get() {
    val max = max(red, max(green, blue))
    val min = min(red, min(green, blue))
    val lightness = (max + min) / 2.0
    val delta = max - min
    return if (delta.toDouble() == 0.0) {
        HSLColor(
            0.0,
            0.0,
            lightness,
        )
    } else {
        val saturation = if (lightness > 0.5) delta / (2.0 - max - min) else delta / (max + min)
        val hue = when (max) {
            red -> (green - blue) / delta + if (green < blue) 6.0 else 0.0
            green -> (blue - red) / delta + 2.0
            else -> (red - green) / delta + 4.0
        }
        HSLColor(
            hue / 6.0,
            saturation as Double,
            lightness,
            alpha.toDouble(),
        )
    }
}

/**
 * Gets the [Color.RGBColor] equivalent to this [HSLColor]
 */
val HSLColor.color: Color get() {
    return if (saturation == 0.0) {
        colorFrom(lightness, lightness, lightness)
    } else {
        val q = if (lightness < 0.5) lightness * (1 + saturation) else lightness + saturation - (lightness * saturation)
        val p = (2.0 * lightness) - q
        fun hue2Rgb(p: Double, q: Double, t: Double): Double {
            val valueT = when {
                t < 0 -> t + 1.0
                t > 1 -> t - 1.0
                else -> t
            }

            return when {
                valueT < (1.0 / 6.0) -> p + ((q - p) * 6.0 * valueT)
                valueT < 0.5 -> q
                valueT < (2.0 / 3.0) -> p + ((q - p) * ((2.0 / 3.0) - valueT) * 6.0)
                else -> p
            }
        }
        colorFrom(
            hue2Rgb(p, q, hue + (1.0 / 3.0)),
            hue2Rgb(p, q, hue),
            hue2Rgb(p, q, hue - (1.0 / 3.0)),
            alpha.toFloat(),
        )
    }
}

/**
 * Increases the lightness of a [Color.RGBColor] by this factor.
 * @param value the amount by which to increase the lightness. Should range between `0.0` and `1.0`
 */
fun Color.lightenBy(value: Double): Color = hsl.let {
    return it.copy(lightness = ((1.0 - it.lightness) * value) + it.lightness).color
}

/**
 * Increases the lightness of a [Color] by this factor.
 * If this is a [Color.DarkLightColor] each component wll be lightened individually.
 * @param value the amount by which to increase the lightness. Should range between `0.0` and `1.0`
 */
//fun Color.lightenBy(value: Double) = when (this) {
//    is Color.RGBColor -> lightenBy(value)
//    is Color.DarkLightColor -> defaultColor.lightenBy(value) withDarkMode darkColor.lightenBy(value)
//    else -> throw IllegalArgumentException("Unknown Color $this")
//}

/**
 * Decreases the lightness of a [Color.RGBColor] by this factor.
 * @param value the amount by which to decrease the lightness. Should range between `0.0` and `1.0`
 */
fun Color.darkenBy(value: Double): Color = hsl.let {
    return it.copy(lightness = (it.lightness - (it.lightness) * value)).color
}

/**
 * Decreases the lightness of a [Color] by this factor.
 * If this is a [Color.DarkLightColor] each component wll be darkened individually.
 * @param value the amount by which to decrease the lightness. Should range between `0.0` and `1.0`
 */
//fun Color.darkenBy(value: Double) = when (this) {
//    is Color.RGBColor -> darkenBy(value)
//    is Color.DarkLightColor -> defaultColor.darkenBy(value) withDarkMode darkColor.darkenBy(value)
//    else -> throw IllegalArgumentException("Unknown Color $this")
//}


internal fun colorFrom(red: Number, green: Number, blue: Number, alpha: Float = 1.0f) = Color.rgb(red.toInt(),green.toInt(),blue.toInt(),alpha)

internal fun colorFrom(red: Double, green: Double, blue: Double, alpha: Float = 1.0f) = Color.rgb(red,green,blue,alpha)